package com.baijiayun.liveuibase.utils;

import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import androidx.constraintlayout.widget.ConstraintLayout;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.RelativeLayout;

import org.jetbrains.annotations.NotNull;

import java.lang.ref.WeakReference;

/**
 * @author lzd
 * <p>
 * 阴影工具类
 */
public class ShadowUtil {
    private WeakReference<View> needShadowView;
    private WeakReference<ViewGroup> shadowView;
    private int scaleUnitHor, scaleUnitVer;
    private int lastWidth, lastHeight;

    public static ShadowUtil Create() {
        return Create(0, 0);
    }

    /**
     * 在需要设置阴影的 view 尺寸变小 时，自动缩小的速度
     * Warning 注意：传入的数值不可大于 设置阴影的 view 尺寸，否则会出问题
     */
    public static ShadowUtil Create(int scaleUnitHor, int scaleUnitVer) {
        return new ShadowUtil(scaleUnitHor, scaleUnitVer);
    }

    private ShadowUtil(int scaleUnitHor, int scaleUnitVer) {
        this.scaleUnitHor = scaleUnitHor;
        this.scaleUnitVer = scaleUnitVer;
    }

    private void initListener(WeakReference<View> view, WeakReference<ViewGroup> shadowView) {
        if (view != null && view.get() != null) {
            view.get().getViewTreeObserver().addOnGlobalLayoutListener(() -> {
                if (shadowView == null || shadowView.get() == null) {
                    return;
                }
                setViewFamilyClipChildren(shadowView.get());
                updateParentViewSize(shadowView.get(), view.get());
            });
        }
    }

    /**
     * 获取当前 阴影 shadowView
     */
    public View getShadowView() {
        return shadowView == null ? null : shadowView.get();
    }

    /**
     * 获取当前 被添加阴影的 shadowView
     */
    public View getNeedShadowView() {
        return needShadowView == null ? null : needShadowView.get();
    }

    /**
     * 更新阴影尺寸信息
     *
     * @param parentView 阴影父布局
     * @param view       需要设置阴影，正在监听尺寸 的 view
     */
    private void updateParentViewSize(ViewGroup parentView, View view) {
        if (lastWidth != Math.max(view.getWidth() - scaleUnitHor, 0) ||
                lastHeight != Math.max(view.getHeight() - scaleUnitVer, 0)) {
            lastWidth = Math.max(view.getWidth() - scaleUnitHor, 0);
            lastHeight = Math.max(view.getHeight() - scaleUnitVer, 0);
            if (view instanceof RelativeLayout) {
                parentView.setLayoutParams(new RelativeLayout.LayoutParams(lastWidth, lastHeight));
            } else if (view instanceof ConstraintLayout) {
                parentView.setLayoutParams(new ConstraintLayout.LayoutParams(lastWidth, lastHeight));
            }
        }
    }

    private void setViewFamilyClipChildren(ViewGroup view) {
        if (view == null) {
            return;
        }
        view.setClipChildren(false);
        view.setClipToPadding(false);
        ViewParent rooter = view.getParent();
        while (rooter instanceof ViewGroup) {
            ((ViewGroup) rooter).setClipChildren(false);
            ((ViewGroup) rooter).setClipToPadding(false);
            rooter = rooter.getParent();
        }
    }

    /**
     * 阴影 Group 数据结构
     * 作为 {@link ShadowUtil#initShadowView(Context, View)} 的返回值生成
     */
    public static class ShadowGroup {
        /**
         * {@link ShadowUtil#initShadowView(Context, View)} 后的新的 content View
         */
        View contentView;

        /**
         * {@link ShadowUtil#initShadowView(Context, View)} 后的 阴影容器
         */
        View shadowContainer;
        View needShadow;

        private ShadowGroup(View contentView, View shadowContainer, View needShadow) {
            this.contentView = contentView;
            this.shadowContainer = shadowContainer;
            this.needShadow = needShadow;
        }

        public View getContentView() {
            return contentView;
        }

        public View getShadowContainer() {
            return shadowContainer;
        }

        public View getNeedShadow() {
            return needShadow;
        }
    }

    /**
     * 初始化需要添加阴影的 View
     *
     * @param needShadow 需要添加阴影的View ，注意：这个 View 不能有 parent
     * @return 阴影的容器，需要再调用 {@link ShadowUtil#setViewBoundShadow(View)}
     * 方法对此返回值设置阴影
     */
    public static ShadowGroup initShadowView(Context context, View needShadow) {
        if (needShadow.getParent() != null) {
            return null;
        }

        ConstraintLayout contentView = new ConstraintLayout(context);
        contentView.setLayoutParams(new ConstraintLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));

        // region 初始化需要添加阴影的 view 的相关参数
        ConstraintLayout.LayoutParams needShadowViewParams;
        if (needShadow.getLayoutParams() instanceof ConstraintLayout.LayoutParams) {
            needShadowViewParams = (ConstraintLayout.LayoutParams) needShadow.getLayoutParams();
        } else {
            needShadowViewParams = new ConstraintLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        }
        needShadowViewParams.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;
        needShadowViewParams.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;
        needShadowViewParams.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;
        needShadowViewParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
        needShadowViewParams.setMargins(
                DisplayUtils.dip2px(context, 8),
                DisplayUtils.dip2px(context, 8),
                DisplayUtils.dip2px(context, 8),
                DisplayUtils.dip2px(context, 8)
        );
        needShadow.setLayoutParams(needShadowViewParams);
        if (needShadow.getId() == View.NO_ID) {
            needShadow.setId(View.generateViewId());
        }
        contentView.addView(needShadow);
        // endregion

        // region 初始化 shadow container
        RelativeLayout shadowContainer = new RelativeLayout(context);
        ConstraintLayout.LayoutParams shadowParams = new ConstraintLayout.LayoutParams(0, 0);
        shadowParams.startToStart = needShadow.getId();
        shadowParams.endToEnd = needShadow.getId();
        shadowParams.topToTop = needShadow.getId();
        shadowParams.bottomToBottom = needShadow.getId();
        shadowContainer.setLayoutParams(shadowParams);
        contentView.addView(shadowContainer);
        // endregion

        return new ShadowGroup(contentView, shadowContainer, needShadow);
    }

    /**
     * 默认 设置阴影的方法
     *
     * @return 阴影相关结果集
     */
    public static ShadowUtil setViewBoundShadow(@NotNull View view) {
        return setViewBoundShadow(view, ShadowUtil.Create());
    }

    /**
     * 默认 设置阴影的方法
     *
     * @param shadowUtilValues 可使用 {@link ShadowUtil#Create(int, int)} 传入 hor 和 ver
     * @return 阴影相关结果集
     */
    public static ShadowUtil setViewBoundShadow(@NotNull View view, @NotNull ShadowUtil shadowUtilValues) {
        //region 默认缩放速度
        if (shadowUtilValues.scaleUnitVer == 0) {
            shadowUtilValues.scaleUnitVer = DisplayUtils.dip2px(view.getContext(), 2);
        }
        if (shadowUtilValues.scaleUnitHor == 0) {
            shadowUtilValues.scaleUnitHor = DisplayUtils.dip2px(view.getContext(), 2);
        }
        //endregion

        int shadowSize = DisplayUtils.dip2px(view.getContext(), 8);

        // 默认阴影颜色
        int startColor = Color.parseColor("#00000000");
        int endColor = Color.parseColor("#050F0F0F");
        return setViewBoundShadow(view, shadowSize, startColor, endColor, shadowUtilValues);
    }

    /**
     * 为 view 设置阴影
     * <b>#Warning: 使用此方法后 viewTree的父容器 的 ClipChildren 属性会置true
     * 这里可能会导致某些父布局里的元素超出容器，后期需要优化</b>
     *
     * @param view             需要设置阴影的 view
     *                         <b>如果是 ViewGroup 需要是 relativeLayout 或 ConstrainLayout</b>
     * @param shadowSize       阴影宽度
     * @param startColor       远离 view 一侧的色值
     * @param endColor         贴近 view 一侧的色值
     * @param shadowUtilValues 在需要设置阴影的 view 尺寸变小 时，自动缩小的速度 信息
     * @return 失败 null ； 成功则返回阴影 所属的 parentView，可通过设置 visibility 控制是否显示
     * 为 view 设置阴影
     * <p>
     * #Note 使用此方法后 若导致某一父容器下的子控件超出，可在适当的位置 将容器 ClipChildren 属性置 false
     */
    public static ShadowUtil setViewBoundShadow(@NotNull View view, int shadowSize
            , int startColor, int endColor, @NotNull ShadowUtil shadowUtilValues) {
        if (shadowSize < 0) {
            return null;
        }
        if (shadowUtilValues.getShadowView() != null) {
            // 已有阴影，移除之前的阴影，并刷新数据
            ((ViewGroup) shadowUtilValues.getShadowView().getParent())
                    .removeView(shadowUtilValues.getShadowView());
            shadowUtilValues.shadowView = null;
            shadowUtilValues.needShadowView = null;
            shadowUtilValues.lastHeight = shadowUtilValues.lastWidth = 0;
        }

        ViewGroup parentView = new RelativeLayout(view.getContext());
        parentView.setId(View.generateViewId());
        if (view instanceof RelativeLayout || view instanceof ConstraintLayout) {
            ((ViewGroup) view).addView(parentView, new ViewGroup.LayoutParams(
                    Math.max(view.getWidth() - shadowUtilValues.scaleUnitHor, 0),
                    Math.max(view.getHeight() - shadowUtilValues.scaleUnitVer, 0)
            ));
            if (view instanceof ConstraintLayout) {
                ConstraintLayout.LayoutParams params = new ConstraintLayout.LayoutParams(0, 0);
                params.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;
                params.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;
                parentView.setLayoutParams(params);
            }
        } else if (!(view instanceof ViewGroup)) {
            parentView.addView(view);
        } else {
            return null;
        }

        class childValue {
            int width;
            int height;
            int horPos;
            int verPos;
            GradientDrawable.Orientation orientation;

            public childValue(int width, int height, int horPos, int verPos, GradientDrawable.Orientation orientation) {
                this.width = width;
                this.height = height;
                this.horPos = horPos;
                this.verPos = verPos;
                this.orientation = orientation;
            }
        }

        childValue[] childValues = new childValue[]{
                new childValue(shadowSize, shadowSize, 1, 1, null),
                new childValue(shadowSize, shadowSize, -1, 1, null),
                new childValue(shadowSize, shadowSize, -1, -1, null),
                new childValue(shadowSize, shadowSize, 1, -1, null),
                new childValue(shadowSize, 0, 1, 0
                        , GradientDrawable.Orientation.RIGHT_LEFT),
                new childValue(shadowSize, 0, -1, 0
                        , GradientDrawable.Orientation.LEFT_RIGHT),
                new childValue(0, shadowSize, 0, -1
                        , GradientDrawable.Orientation.TOP_BOTTOM),
                new childValue(0, shadowSize, 0, 1
                        , GradientDrawable.Orientation.BOTTOM_TOP),
        };
        int[] dirViewsId = new int[4];

        for (int i = 0; i < childValues.length; i++) {
            // region 尺寸
            RelativeLayout.LayoutParams childParams
                    = new RelativeLayout.LayoutParams(childValues[i].width, childValues[i].height);
            View child = new View(view.getContext());
            //endregion

            // region 位置
            if (childValues[i].horPos == 1) {
                childParams.addRule(RelativeLayout.ALIGN_PARENT_END);
                childParams.rightMargin = - shadowSize - shadowUtilValues.scaleUnitHor;
            } else if (childValues[i].horPos == -1) {
                childParams.leftMargin = - shadowSize;
            }

            if (childValues[i].verPos == 1) {
                childParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
                childParams.bottomMargin = - shadowSize - shadowUtilValues.scaleUnitVer;
            } else if (childValues[i].verPos == -1) {
                childParams.topMargin = - shadowSize;
            }

            if (i < dirViewsId.length) {
                // 前四个为四个角
                child.setId(View.generateViewId());
                dirViewsId[i] = child.getId();
            } else {
                // 后四个以四个角为基准来显示
                if (childValues[i].horPos == 1 && childValues[i].verPos == 0) {
                    childParams.addRule(RelativeLayout.ABOVE, dirViewsId[0]);
                    childParams.addRule(RelativeLayout.BELOW, dirViewsId[3]);
                } else if (childValues[i].horPos == -1 && childValues[i].verPos == 0) {
                    childParams.addRule(RelativeLayout.ABOVE, dirViewsId[1]);
                    childParams.addRule(RelativeLayout.BELOW, dirViewsId[2]);
                } else if (childValues[i].horPos == 0 && childValues[i].verPos == -1) {
                    childParams.addRule(RelativeLayout.LEFT_OF, dirViewsId[3]);
                    childParams.addRule(RelativeLayout.RIGHT_OF, dirViewsId[2]);
                } else if (childValues[i].horPos == 0 && childValues[i].verPos == 1) {
                    childParams.addRule(RelativeLayout.LEFT_OF, dirViewsId[0]);
                    childParams.addRule(RelativeLayout.RIGHT_OF, dirViewsId[1]);
                }
            }
            //endregion

            // region 背景图
            GradientDrawable childBg = new GradientDrawable();
            if (childValues[i].orientation != null) {
                childBg.setColors(new int[]{startColor, endColor});
                childBg.setOrientation(childValues[i].orientation);
            } else {
                childBg.setColors(new int[]{endColor, startColor});
                childBg.setGradientRadius(shadowSize);
                childBg.setGradientCenter(Math.max(0, -childValues[i].horPos)
                        , Math.max(0, -childValues[i].verPos));
                childBg.setGradientType(GradientDrawable.RADIAL_GRADIENT);
                childBg.setCornerRadii(new float[]{
                        childValues[i].horPos + childValues[i].verPos == -2 ? shadowSize : 0,
                        childValues[i].horPos + childValues[i].verPos == -2 ? shadowSize : 0,
                        childValues[i].horPos == 1 && childValues[i].verPos == -1 ? shadowSize : 0,
                        childValues[i].horPos == 1 && childValues[i].verPos == -1 ? shadowSize : 0,
                        childValues[i].horPos + childValues[i].verPos == 2 ? shadowSize : 0,
                        childValues[i].horPos + childValues[i].verPos == 2 ? shadowSize : 0,
                        childValues[i].horPos == -1 && childValues[i].verPos == 1 ? shadowSize : 0,
                        childValues[i].horPos == -1 && childValues[i].verPos == 1 ? shadowSize : 0
                });
            }
            //endregion

            child.setBackground(childBg);
            child.setLayoutParams(childParams);

            parentView.addView(child);
        }

        shadowUtilValues.shadowView = new WeakReference<>(parentView);
        shadowUtilValues.needShadowView = new WeakReference<>(view);

        shadowUtilValues.initListener(new WeakReference<>(view)
                , new WeakReference<>(parentView));

        return shadowUtilValues;
    }

    /**
     * 重新注册阴影的尺寸监听，用处：
     * 例如：在 popupWindow 被 dismiss 后，视图的 GlobalLayoutListener 会被移除，
     * 如果重新 show 后尺寸会发生改变，则无法监听到，建议在 show 时调用此方法重新监听；
     * 如果重新 show 后尺寸不再改变，则无需调用此方法。
     *
     * @param shadowUtilValues 需要传入 {@link ShadowUtil#setViewBoundShadow(View)} 等方法的返回值
     */
    public static void reInit(ShadowUtil shadowUtilValues) {
        if (shadowUtilValues.needShadowView == null || shadowUtilValues.needShadowView.get() == null
                || shadowUtilValues.shadowView == null || shadowUtilValues.shadowView.get() == null) {
            return;
        }
        shadowUtilValues.lastHeight = shadowUtilValues.lastWidth = 0;
        shadowUtilValues.initListener(new WeakReference<>(shadowUtilValues.getNeedShadowView())
                , new WeakReference<>((ViewGroup) shadowUtilValues.getShadowView()));
        shadowUtilValues.needShadowView.get().requestLayout();
    }
}
